﻿using System;
using System.Runtime.InteropServices;
using SCG = System.Collections.Generic;

using Nemerle.Assertions;
using Nemerle.Utility;

using GASS.OpenCL;
using GASS.OpenCL.OpenCLDriver;

namespace Nemerle.OpenCL {

    /// <summary>
    /// Description of Memory.
    /// </summary>
    [InfoProperty(CLMemObjectInfo.ReferenceCount, int, Flags(no_cache))]
    [InfoProperty(CLMemObjectInfo.MapCount, int, Flags(no_cache))]
    [InfoProperty(CLMemObjectInfo.Type, CLMemObjectType)]
    [InfoProperty(CLMemObjectInfo.Size, int)]
    [EqualityByIdPattern]
    public abstract class Memory : IDisposable {

        internal this([NotNull] context : Context, host_ptr : object, flags : CLMemFlags) {
            this.context = context;
            if(null != host_ptr) {
                this.flags = flags | CLMemFlags.UseHostPtr;
                this.host_ptr = host_ptr;
                this.host_ptr_handle = GCHandle.Alloc(host_ptr, GCHandleType.Pinned);
            } else {
                this.flags = flags & ~CLMemFlags.UseHostPtr;
            }
        }

        [Accessor] context : Context;

        [Accessor] flags : CLMemFlags;

        host_ptr_handle : GCHandle;

        [Accessor] host_ptr : object;

        mutable handle : MemObjectHandle;

        public Id : CLMem {
            get { handle.EntityId }
        }

        protected Initialize() : void {
            try {
                def host_ptr =
                    if(host_ptr_handle.IsAllocated)
                        host_ptr_handle.AddrOfPinnedObject();
                    else
                        IntPtr.Zero;
                this.handle = MemObjectHandle(CreateHandle(host_ptr));
            } catch {
                | e => Dispose(); throw e;
            }
        }

        protected abstract CreateHandle(host_ptr : IntPtr) : CLMem;

        #region IDisposable

        public Dispose() : void {
            when(null != handle)
                handle.Close();
            when(host_ptr_handle.IsAllocated)
                host_ptr_handle.Free();
        }

        #endregion


    }

}